<?php
/**
 * ################################################
 * #【Description】 支付接口，用户小程序使用微信支付
 * #【Author】 贾瑞一
 * #【Date】  2017年8月17日
 * ################################################
 */
namespace Shx\Controller;
use Think\Controller;

class PayController extends Controller{

    /**
     * 生成微信支付订单
     */
    public function makeWechatOrder(){
        if(IS_POST){
            $receive = json_decode(file_get_contents('php://input'),true);
            $data = array(
                'order_sn' => $receive['order_id'],
                'user_id'  => $receive['user_id'],
                'amount'   => $receive['amount'] * 100,
                'create_time' => time()
            );
            $id = M('wechat_order')->add($data);

            if($id){
                $this->pay($receive['amount'] * 100, $receive['open_id'], $receive['order_id']);
            }else{
                $res = array(
                    'status' => 0,
                    //'msg'    => '系统错误，请稍后重试'
                    'msg'    => '微信支付订单表数据写入失败'
                );
                exit(json_encode($res, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
            }
        }else{
            $res = array(
                'status' => 0,
                'msg'    => '系统错误，请稍后重试'
            );
            exit(json_encode($res, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
        }
    }

    /**
     * 微信统一下单
     */
    public function pay($amount, $open_id, $order_id){
        if(empty($amount)){
            exit(json_encode(array('status'=>0,'msg'=>'金额有误'),JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
        }else if(empty($open_id)){
            exit(json_encode(array('status'=>0,'msg'=>'登录失败，请重新登录'),JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
        }else if(empty($order_id)){
            exit(json_encode(array('status'=>0,'msg'=>'自定义订单失败'),JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
        }

        $appid  = 'wx8c5872c872232527';                                                 //小程序appid
        $body   = '测试';
        $mch_id = '10096807';                                                           //商户账号
        $key    = 'aks8jdhr91ien5fuw9ci4us02mf84hdi';                                   //微信支付的key
        $nonce_str    = randomKeys(32);                                                 //随机字符串
        $notify_url   = 'https://api.shenhexin.com/Shx/Pay/notify_me';                  //支付完成回调地址，不能带参数
        $out_trade_no = $order_id;                                                     //商户订单号
        $spbill_create_ip = $_SERVER['SERVER_ADDR'];
        $trade_type       = 'JSAPI';
        $post['appid']  = $appid;
        $post['body']   = $body;
        $post['mch_id'] = $mch_id;
        $post['nonce_str']  = $nonce_str;
        $post['notify_url'] = $notify_url;
        $post['openid']     = $open_id;
        $post['out_trade_no']     = $out_trade_no;
        $post['spbill_create_ip'] = $spbill_create_ip;
        $post['total_fee']  = intval($amount);         //最低为一分钱，必须是整数
        $post['trade_type'] = $trade_type;
        $sign = $this->makeSign($post,$key);           //签名
        $this->sign = $sign;

        $post_xml = '<xml>
                        <appid>'.$appid.'</appid>
                        <body>'.$body.'</body>
                        <mch_id>'.$mch_id.'</mch_id>
                        <nonce_str>'.$nonce_str.'</nonce_str>
                        <notify_url>'.$notify_url.'</notify_url>
                        <openid>'.$open_id.'</openid>
                        <out_trade_no>'.$out_trade_no.'</out_trade_no>
                        <spbill_create_ip>'.$spbill_create_ip.'</spbill_create_ip>
                        <total_fee>'.$amount.'</total_fee>
                        <trade_type>'.$trade_type.'</trade_type>
                        <sign>'.$sign.'</sign>
                    </xml>';
        //统一下单接口获取prepay_id
        $url = 'https://api.mch.weixin.qq.com/pay/unifiedorder';
        $xml = $this->http_request($url,$post_xml);
        $array = $this->xmlToArray($xml);
//        p($array);
        if($array['RETURN_CODE'] == 'SUCCESS'){
            $time = time();
            $tmp = '';
            $tmp['appId'] = $appid;
            $tmp['nonceStr'] = $nonce_str;
            $tmp['package'] = 'prepay_id='.$array['PREPAY_ID'];
            $tmp['signType'] = 'MD5';
            $tmp['timestamp'] = '$time';

            $data['status'] = 1;
            $data['timeStamp'] = "$time";
            $data['nonceStr'] = $nonce_str;
            $data['signType'] = 'MD5';
            $data['package'] = 'prepay_id='.$array['PREPAY_ID'];
            //$data['paySign'] = $this->makeSign($tmp, $key);
            $data['paySign'] = $this->sign;
            $data['out_trade_no'] = $out_trade_no;
        }else{
            $data['status'] = 0;
            $data['msg'] = '系统繁忙，请稍后重试';
            $data['RETURN_CODE'] = $array['RETURN_CODE'];
            $data['RETURN_MSG'] = $array['RETURN_MSG'];
        }
//        p($data);exit;
        echo json_encode($data);
    }

    /**
     * 支付签名
     * @param $params
     * @param $key
     */
    public function makeSign($params, $key){
        //一、按字典排序数组参数
        ksort($params);

        $string = $this->ToUrlParams($params);

        //二、在string后面加入key
        $string = $string . "&key=" . $key;
        //三、MD5加密
        $string = md5($string);
        //四、所有字符转大写
        $result = strtoupper($string);

        return $result;
    }

    /**
     * 将参数拼接为url: key=value&key=value
     * @param $params
     *
     * @return string
     */
    public function toUrlParams($params){
        $string = '';
        if(!empty($params)){
            $array = array();
            foreach($params as $key=>$value){
                $array[] = $key .'='.$value;
            }
            $string = implode("&",$array);
        }
        return $string;
    }
    /*public function ToUrlParams($params){
        $buff = '';
        foreach($params as $k=>$v){
            if($k != "sign" && $v != "" && !is_array($v)){
                $buff .= $k . "=" . $v . "&";
            }
        }
        $buff = trim($buff, "&");
        return $buff;
    }*/

    private function formatBizQueryParaMap($paraMap, $urlencode){
        $buff = "";
        ksort($paraMap);
        foreach($paraMap as $k=>$v){
            if($urlencode){
                $v = urlencode($v);
            }
            $buff .= $k . "=" . $v . "&";
        }
        if(strlen($buff) > 0){
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }
        return $reqPar;

    }
    /**
     * 调用接口， $data是数组参数
     * @param $url
     * @param null $data
     * @param array $headers
     *
     * @return mixed 签名
     */
    public function http_request($url, $data = null, $headers = array()){
        $curl = curl_init();
        if(count($headers) >= 1){
            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
        }
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);

        if(!empty($data)){
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($curl);
        curl_close($curl);
        return $output;
    }

    //获取xml里面的数据，转换成array
    private function xmlToArray($xml){
        $p = xml_parser_create();
        xml_parse_into_struct($p, $xml, $vals, $index);
        xml_parser_free($p);
        $data = "";
        foreach($index as $key=>$value){
            if($key == 'xml' || $key == 'XML') continue;
            $tag = $vals[$value[0]]['tag'];
            $value = $vals[$value[0]]['value'];
            $data[$tag] = $value;
        }
        return $data;
    }

    /**
     * 将xml转为array
     * @param $xml
     *
     * @return bool|mixed
     */
    public function xml_to_array($xml){
        if(!$xml){
            return false;
        }
        //将XML转为array，禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement',LIBXML_NOCDATA)),true);
        return $data;
    }
    /**
     * 微信支付完成，回调地址
     */
    public function notify_me(){
        $post = file_get_contents('php://input');
        $post_data = $this->xml_to_array($post);
        $postSign = $post_data['sign'];
        unset($post_data['sign']);
        /* 微信官方提醒：
         *  商户系统对于支付结果通知的内容一定要做【签名验证】,
         *  并校验返回的【订单金额是否与商户侧的订单金额】一致，
         *  防止数据泄漏导致出现“假通知”，造成资金损失。
         */
        ksort($post_data);                           // 对数据进行排序
        $str = $this->toUrlParams($post_data);      //数组拼接成key=key字符串
        $user_sign = strtoupper(md5($post_data));   //再次生成签名，与$postSign比较

        $where['order_sn'] = $post_data['out_trade_no'];

        $order_status = M('wechat_order')->where($where)->find();

        if($post_data['return_code'] == 'SUCCESS' && $postSign){
            /*
             * 首先判断，订单是否已经更新为ok，因为微信会总共发送8次回调确认
             * 其次，订单已经为ok的，直接返回SUCCESS
             * 最后，订单没有为ok的，更新状态为ok，返回SUCCESS
             */
            if($order_status['is_pay']){
                $this->return_success();
            }else{
                $updata['is_pay'] = 1;
                if(M('wechat_order')->where($where)->save($updata)){
                    $this->return_success();
                }
            }
        }else{
            echo '微信支付失败';
        }
    }

    /*
     * 给微信发送确认订单金额和签名正确
     */
    private function return_success(){
        $str = 'appid=wx8c5872c872232527&body=申合信商城-办公用品&mch_id=10096807&nonce_str=cfgnprstuxyzCFGGKNRUWXZ023456789&notify_url=https://api.shenhexin.com/index.php/Shx/Pay/notify_me&openid=oP28e0WqAPKfmam6D96AI1aYbGD0&out_trade_no=20170817161121&spbill_create_ip=172.18.187.185&total_fee=10&trade_type=JSAPI&key=6a3366bf914a115b39366c165306cb27';

        $return['return_code'] = 'SUCCESS';
        $return['return_msg'] = 'OK';
        $xml_post = '<xml>
                         <return_code>'.$return['return_code'].'</return_code>
                         <return_msg>'.$return['return_msg'].'</return_msg>
                     </xml>';
        echo $xml_post;
        exit;
    }


    /**
     * 用户支付结果记录
     * 支付状态(pay_status): 0-->未支付   1-->待支付   2-->取消支付  3-->支付失败  4-->已支付
     */
    public function takePayStatus(){
        $receive = json_decode(file_get_contents('php://input'), true);
        //1、订单表状态更新
        $order_id = M('order')->where(array('order_sn'=>$receive['order_id']))->field('order_id')->find();
        $is_update_order = M('order')->where(array('order_sn'=>$receive['order_id']))->setField(array('pay_status'=>$receive['pay_status'],'pay_time'=>time()));
        //2、订单日志表状态更新
        if($receive['pay_status'] == 0){
            $status_desc = '未支付';
        }elseif($receive['pay_status'] == 1){
            $status_desc = '待支付';
        }elseif($receive['pay_status'] == 2){
            $status_desc = '取消支付';
        }elseif($receive['pay_status'] == 3){
            $status_desc = '支付失败';
        }elseif($receive['pay_status'] == 4){
            $status_desc = '已支付';
        }else{
            $status_desc = '未知错误，支付失败';
        }
        $order_action = array(
            'order_id' => $order_id['order_id'],
            'user_id'  => $receive['user_id'],
            'order_status' => $receive['pay_status'],
            'pay_status'   => $receive['pay_status'],
            'action_note'  => $receive['msg'],
            'status_desc'  => $status_desc,
            'log_time'     => time()
        );
        $is_insert_order_action = M('order_action')->add($order_action);

        if(!$is_update_order){
            $res = array(
                'status' => 0,
                'msg'    => '订单更新失败'
            );
        }elseif(!$is_insert_order_action){
            $res = array(
                'status' => 0,
                'msg'    => '订单日志表插入数据失败'
            );
        }else{
            $res = array(
                'status' => 1,
                'msg'    => 'success'
            );
        }
        exit(json_encode($res, JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));
    }
}